/*system header*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <stdint.h>
/*user header*/
//#include <thread.h>

/* Thread handler function for pthread_create */
void* handler_create(void *arg){
    pthread_t tid = pthread_self();
    printf("I am thread %ld\n",(long int)tid);

    /* Check if argument is NULL */
    char *str = (char*)arg;
    if(str == NULL){
        printf("nothing recived form str\n");
        pthread_exit((void*)-1);
    }

    /* Print received message */
    printf("message form pthread_create:\n%s\n",str);
    return (void*)0;
}

/* Main function to demonstrate thread creation and joining */
int thread_pthread_func(int argc, char *argv[]){
    /* Check command line arguments */
    if(argc < 2){
        printf("Usage:%s <message to thread>\n", argv[0]);
        return -1;
    }

    /* Handle NULL argument case */
    char *str = (strcmp(argv[1],"NULL") == 0) ? NULL : argv[1]; 

    /* Create new thread */
    pthread_t pth;
    int err = pthread_create(&pth, NULL, handler_create, str);
    if(err != 0){
        printf("pthread_create error:%s\n", strerror(err));
        return -1;
    }

    /* Wait for thread to complete and get return value */
    void *ret = NULL ;
    int join = pthread_join(pth, &ret);
    if(join != 0){
        printf("pthread_join error: %s\n", strerror(join));
        return -1;
    }
    printf("thread %ld exit status: %ld\n", (long int)pth, (intptr_t)ret);

    return 0;
}

/* Shared data protected by mutex */
static long int data_mutex;
/* Mutex for protecting data_mutex */
static pthread_mutex_t mtx_lock_unlock = PTHREAD_MUTEX_INITIALIZER;

/* Thread function that increments shared data with mutex protection */
void *handler_mutex_lock_unlock(void *num){
    /* Convert argument to loop count */
    int loop = atoi((char*)num);
    int ret, tmp;

    /* Loop to increment shared data */
    for(int i = 0; i < loop; i++){
        /* Lock mutex before accessing shared data */
        ret = pthread_mutex_lock(&mtx_lock_unlock);
        if(ret != 0){
            printf("pthread_mutex_lock error :%s\n",strerror(ret));
            return (void*)-1;
        }

        /* Critical section: increment shared data */
        tmp = data_mutex;
        tmp++;
        data_mutex = tmp;

        /* Unlock mutex after accessing shared data */
        ret = pthread_mutex_unlock(&mtx_lock_unlock);
        if(ret != 0){
            printf("pthread_mutex_unlock error:%s\n", strerror(ret));
            return (void*)-1;
        }
    }
    return (void*)0;
}

/* Function to create multiple threads that increment shared data */
int thread_mutex_lock_unlock(int argc, char *argv[]){
    /* Check command line arguments */
    if(argc < 2 || atoi(argv[1]) < 0){
        printf("Usage:%s <loop times>", argv[0]);
        return -1;
    }

    /* Create 5 threads */
    pthread_t thr[5];
    int errn;
    for(int i = 0; i < 5; i++){
        errn = pthread_create(&thr[i], NULL, handler_mutex_lock_unlock, (void*)argv[1]);
        if(errn != 0){
            printf("pthread_create error:%s\n", strerror(errn));
            return -1;
        }
    }

    /* Wait for all threads to complete */
    void *ret = NULL;
    for(int j = 0; j < 5; j++){
        if(pthread_join(thr[j], &ret) != 0){
            printf("pthread_join error\n");
            return -1;
        }
        if((intptr_t)ret != 0)
            printf("thread %ld exit error\n", (long int)thr[j]);
    }

    /* Print final value of shared data */
    printf("the value of \"data_mutex\" is %ld \n", data_mutex);
    return 0;
}

/*mutex to protect shared data*/
static pthread_mutex_t mtx_mutex_init;
/* Shared data protected by mutex */
static long int data_mutex_init = 0;

void *handler_mutex_init(){
    // Lock mutex before accessing shared data
    int ret = pthread_mutex_lock(&mtx_mutex_init);
    if(ret != 0){
        printf("pthread_lock error :%s\n", strerror(ret));
        return (void*)-1;
    }

    // Critical section: increment shared data
    long int tmp = data_mutex_init;
    for(int i = 0; i < 100000; i++)
        tmp ++;
    data_mutex_init = tmp;

    // Unlock mutex after accessing shared data
    ret = pthread_mutex_unlock(&mtx_mutex_init);
    if(ret != 0){
        printf("pthread_unlock error :%s\n", strerror(ret));
        return (void*)-1;
    }
    return (void*)0;
}

int thread_mutex_init(){
    // Initialize mutex
    int ret = pthread_mutex_init(&mtx_mutex_init, NULL);
    if(ret != 0){
        printf("pthread_mutex_init error: %s\n", strerror(ret));
        return -1;
    }

    // Create first thread
    pthread_t pth_1, pth_2;
    ret = pthread_create(&pth_1, NULL, handler_mutex_init, NULL);
    if(ret != 0){
        printf("pthread_create error:%s\n", strerror(ret));
        return -1;
    }

    // Create second thread
    ret = pthread_create(&pth_2, NULL, handler_mutex_init, NULL);
    if(ret != 0){
        printf("pthread_create error:%s\n", strerror(ret));
        goto clean_up;
    }

    // Wait for threads to complete and check their status
    void *err;
    if(pthread_join(pth_1, &err) != 0){
        printf("pthread_join error\n");
        goto clean_up;
    }
    if((intptr_t)err != 0)
        printf("thread %ld exit error\n", (long int)err);

    if(pthread_join(pth_2, &err) != 0){
        printf("pthread_join error\n");
        goto clean_up;
    }
    if((intptr_t)err != 0)
        printf("thread %ld exit error\n", (long int)err);
    
    clean_up:
        // Clean up mutex resources
        if(pthread_mutex_destroy(&mtx_mutex_init) != 0){
            printf("pthread_mutes_destory error\n");
            return -1;
        }
        
    // Print final value of shared data
    printf("the value of data_mutex_init is %ld\n", data_mutex_init);
    return 0;
}

/* Mutex for producer-consumer synchronization */
pthread_mutex_t mtx_cp = PTHREAD_MUTEX_INITIALIZER;
/* Condition variable for producer-consumer synchronization */
pthread_cond_t con_cp = PTHREAD_COND_INITIALIZER;
/* Count of available products */
static int available = 0;
/* Count of consumed products */
static int consumed = 0;

/* Producer thread function that creates products */
void *handler_productor(){
    sleep(1);
    /* Lock mutex before accessing shared data */
    int err = pthread_mutex_lock(&mtx_cp);
    if(err != 0){
        printf("mutex lock error: %s\n", strerror(err));
        return (void*)-1;
    }

    /* Increment available products count */
    available++;
    printf("one product produced, we will inform the consumer\n");

    /* Signal consumer that product is available */
    err = pthread_cond_signal(&con_cp);
    if(err != 0){
        printf("cont signal error: %s\n", strerror(err));
        pthread_mutex_unlock(&mtx_cp);
        return (void*)-1;
    }

    /* Unlock mutex after accessing shared data */
    err = pthread_mutex_unlock(&mtx_cp);
    if(err != 0){
        printf("mutex unlock error: %s\n", strerror(err));
        return (void*)-1;
    }

    return (void*)0;
}

/* Consumer thread function that consumes products */
void *handler_consumer(){
    /* Lock mutex before checking shared data */
    int err = pthread_mutex_lock(&mtx_cp);
    if(err != 0){
        printf("mutex lock error:%s\n", strerror(err));
        return (void*)-1;
    }

    /* Wait while no products are available */
    while(available <= 0){
        err = pthread_cond_wait(&con_cp, &mtx_cp);
        if(err != 0){
            printf("cont wait error:%s\n", strerror(err));
            pthread_mutex_unlock(&mtx_cp);
            return (void*)-1;
        }
    }

    /* Consume product and update counters */
    available--;
    consumed++;
    printf("one product has benn consumed\n");

    /* Unlock mutex after accessing shared data */
    err = pthread_mutex_unlock(&mtx_cp);
    if(err != 0){
        printf("mutex unlock error:%s\n", strerror(err));
        return (void*)-1;
    }

    return (void*)0;
}

/* Main function for producer-consumer demonstration */
int thread_producter_consumer(int argc, char *argv[]){
    /* Check command line arguments */
    if(argc < 2 || atoi(argv[1]) <= 0){
        printf("Usage:%s <consumer number>\n", argv[0]);
        return -1;
    }

    int err, tmp = atoi(argv[1]);

    /* Create producer and consumer threads */
    pthread_t pth_p[tmp],pth_c[tmp];
    for(int i = 0; i < tmp; i++){
        err = pthread_create(&pth_p[i], NULL, handler_productor, NULL);
        if(err != 0){
            printf("thread created error:%s\n", strerror(err));
            return -1;
        }
        err = pthread_create(&pth_c[i], NULL, handler_consumer, NULL);
        if(err != 0){
            printf("thread created error:%s\n", strerror(err));
            return -1;
        }
    }

    /* Wait for all threads to complete */
    for(int i = 0; i < tmp; i++){
        pthread_join(pth_p[i], NULL);
        pthread_join(pth_c[i], NULL);
    }

    /* Print total consumed products */
    printf("the consumer has totally consumed %d producteds\n", consumed);
    return 0;
}